Mac上交叉编译ffmpeg For Android

介绍通过mac os系统编译给Android平台上可以使用的ffmpeg静态库。

[TOC]

shell脚本

编译ffmpeg的时候,需要在命令行上,配置编译的路径以及设置不同的属性值,如果一个个命令行输入的话,很容易出错,出错的时候修改后所有命令重新输出也麻烦,所以,一般都是写在shell脚本中进行批处理的。

mac os系统默认的shell为bash,我个人使用为shell的终极神器–zsh,具体优点不详细介绍。可以通过命令查看当前平台上的shell解析器

1
2
3
4
5
6
7
➜  cat /etc/shells
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
  • Shell 教程 | 菜鸟教程

    shell脚本的编写,可以参考上面的教程,快速看一下大概,接下来用到再详细去查就好了

下载ffmpeg源码

  • 直接在ffmpage官网,点击download,直接下载最新的release包。

  • 解压.tar.bz2文件

    以前用mac的解压工具出现文件损坏,这里直接采用命令行解压

    1
    ➜  tar xvf ffmpeg-4.0.2.tar.bz2

编写shell脚本

  • 进入解压后的文件路径

    1
    ➜  cd ffmpeg-4.0.2
  • 查看帮助信息

    1
    ➜  ffmpeg-4.0.2 ./configure --help

    ffmpeg 的编译选项翻译

    输出的编译配置属性,可以参考该链接的大概介绍,或者直接看英文也能知道个大概。

  • 编写shell脚本内容

    接下来会再详细介绍shell脚本内容,也可以参考上面的ffmpeg 的编译选项翻译设置不同的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #!/bin/bash

    ./configure \
    --prefix=./androidLibs/armeabi-v7 \
    --enable-small \
    --disable-programs \
    --disable-avdevice \
    --disable-encoders \
    --disable-muxers \
    --disable-filters \
    --enable-cross-compile \
    --cross-prefix=/Users/guidongyuan/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi- \
    --disable-shared \
    --enable-static \
    --sysroot=/Users/guidongyuan/Library/Android/sdk/ndk-bundle/platforms/android-21/arch-arm \
    --extra-cflags="-isystem /Users/guidongyuan/Library/Android/sdk/ndk-bundle/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -O0 -fPIC" \
    --extra-cflags="-isysroot /Users/guidongyuan/Library/Android/sdk/ndk-bundle/sysroot" \
    --arch=arm \
    --target-os=android

    # 不建议直接把make写在sh文件中
    # make clean
    # make install
  • 执行shell脚本

    检查环境是否正常没有错误

    1
    ➜  ffmpeg-4.0.2 ./buildffmpeg.sh

    如果提示权限错误,修改权限重新执行

    1
    2
    3
    4
    5
    # 出现权限错误
    ➜ ffmpeg-4.0.2 ./buildffmpeg.sh
    zsh: permission denied: ./test.sh
    # 修改属性为可执行
    ➜ studyshell chmod +x test.sh

    输出结果,如果输出各种环境信息,且提示一个WARNING,不用管它,可以继续执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ➜  ffmpeg-4.0.2 ./buildffmpeg.sh
    install prefix ./androidLibs/armeabi-v7
    source path .
    C compiler /Users/guidongyuan/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc
    C library bionic
    host C compiler gcc
    host C library
    ARCH arm (armv7-a)
    ......省略
  • 执行make命令

    1
    2
    3
    4
    5
    6
    7
    8
    ➜  ffmpeg-4.0.2 make clean
    ➜ ffmpeg-4.0.2 make install
    ......省略
    INSTALL libavutil/avconfig.h
    INSTALL libavutil/ffversion.h
    GEN libavutil/libavutil.pc
    INSTALL libavutil/libavutil.pc
    # 最后如果没有提示错误,则表示编译成功
  • 查看输出结果

    可以看到,根据配置输出对应的.a静态库文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ➜  androidLibs tree armeabi-v7 -L 2
    armeabi-v7
    ├── include
    │   ├── libavcodec
    │   ├── libavfilter
    │   ├── libavformat
    │   ├── libavutil
    │   ├── libswresample
    │   └── libswscale
    ├── lib
    │   ├── libavcodec.a
    │   ├── libavfilter.a
    │   ├── libavformat.a
    │   ├── libavutil.a
    │   ├── libswresample.a
    │   ├── libswscale.a
    │   └── pkgconfig
    └── share
    └── ffmpeg

    11 directories, 6 files
  • 如果需要动态库,可以通过把静态库编译为动态库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ➜  gcc -shared -o 动态库名称.so -Wl,--whole-archive 静态库名称.a -Wl,--no-whole-archive
    # --whole-archive: 将未使用的静态库符号(函数实现)也链接进动态库
    #--no-whole-archive : 默认,未使用不链接进入动态库
    # 实例
    # 因为是编译给Android使用的,所以,必须使用交叉编译,不能直接用gcc编译
    ➜ /Users/guidongyuan/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=/Users/guidongyuan/Library/Android/sdk/ndk-bundle/platforms/android-21/arch-arm --shared -o libFFmpeg.so -Wl,--whole-archive libavcodec.a libavfilter.a libavformat.a libavutil.a libswresample.a libswscale.a -Wl,--no-whole-archive
    # 查看生成的文件,编译成功
    ➜ ls
    libFFmpeg.so libavfilter.a libavutil.a libswscale.a
    libavcodec.a libavformat.a libswresample.a pkgconfig
    ➜ file libFFmpeg.so
    libFFmpeg.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /system/bin/linker, with debug_info, not stripped
  • 把编译好的库移动到Android项目中就可以使用了

    如果把动态库导入到项目中直接使用不再修改的话,那么移动后在项目添加System.loadLibrary();就可以了;如果还需要进行NDK开发的话,就要了解一下CMake的相对知识了,接下来的文章再进行介绍。

shell脚本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash
# #! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序

# 执行configure,后面的为其参数
# \为换行符
./configure \
# 设置生成出来的路径目录,不写则用默认值[/usr/local]
--prefix=./androidLibs/armeabi-v7 \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
# 使用了交叉编译
--enable-cross-compile \
# 为编译工具指定路径,交叉编译,使用arm架构
--cross-prefix=/Users/guidongyuan/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi- \
# 关闭输出动态库
--disable-shared \
# 设置为输出静态库
--enable-static \
# 指定编译的头文件与库文件的查找目录
--sysroot=/Users/guidongyuan/Library/Android/sdk/ndk-bundle/platforms/android-21/arch-arm \
# 传递gcc编译的参数(包括查找头文件路径),该属性可以在Android Stuido中获取
--extra-cflags="-U_FILE_OFFSET_BITS -isystem /Users/guidongyuan/Library/Android/sdk/ndk-bundle/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -O0 -fPIC" \
--extra-cflags="-isysroot /Users/guidongyuan/Library/Android/sdk/ndk-bundle/sysroot" \
# 选择机器架构
--arch=arm \
--target-os=android

–extra-cflags

该值可以在Android Studio的支持NDK项目中复制,创建NDK项目可以参考NDK(一):编写第一个JNI项目%EF%BC%9A%E7%BC%96%E5%86%99%E7%AC%AC%E4%B8%80%E4%B8%AAJNI%E9%A1%B9%E7%9B%AE/),具体路径为

image-20180908172034663

image-20180908172105796

注意:FLAGS属性值的其中的-std=c++11-fno-limit-debug-info在编译的运行上面的.sh文件时提示异常和错误,于是选择把其去掉了。

错误解决

  • 查找错误

    编译的时候,不太可能一次性就成功编译,或多或少会出现异常,就需要查log了,比如:

    1
    2
    3
    4
    5
    6
    # 错误输出
    /Users/guidongyuan/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc is unable to create an executable file.
    C compiler test failed.

    Include the log file "ffbuild/config.log" produced by configure as this will help
    solve the problem.

    出现错误,可以查看config.log的具体内容

    1
    2
    arm-linux-androideabi-gcc: error: unrecognized command line option '-fno-limit-debug-info'
    C compiler test failed.

    可以看到找不到-fno-limit-debug-info命令,原来是我上面设置--extra-cflags属性值在Android Studio复制多了,去掉重新编译就成功了。

  • 修改minSdkVersion版本

    把上面编译好的库放到Android Studio中,运行的时候无法通过,错误为

    1
    2
    3
    4
      libavformat/hls.c:834: error: undefined reference to 'atof'
    libavformat/hlsproto.c:141: error: undefined reference to 'atof'
    libavcodec/v4l2_buffers.c:439: error: undefined reference to 'mmap64'
    clang++: error: linker command failed with exit code 1 (use -v to see invocation)

    出现该错误的原因,是因为上面shell脚本中,指定编译的头文件与库文件的查找目录都是21,这样编译出来的依赖库只能用在minSdkVersion 21的项目中,而我的项目最低是兼容到16,那就要重新修改了16

    修改好再次编译,提示该错误

    1
    2
    3
    4
      libavcodec/v4l2_buffers.c:434:44: error: call to 'mmap' declared with attribute error: mmap is not available with _FILE_OFFSET_BITS=64 when using GCC until android-21. Either raise your minSdkVersion, disable _FILE_OFFSET_BITS=64, or switch to Clang.
    avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length,
    ^
    make: *** [libavcodec/v4l2_buffers.o] Error 1

要不就切换为Clang,要不就disable _FILE_OFFSET_BITS,只能选择后者,在--extra-cflags添加取消宏定义即可。

1
--extra-cflags="-U_FILE_OFFSET_BITS ......省略“

公众号:亦袁非猿

欢迎关注,交流学习